class Func(object):
    registry = {}
    def __init__(self, name, code):
        if name is not None:
            self.registry[name] = self
        self.code = code
    def eval(self, stack):
        return self
    def invoke(self, stack, parentstack):
        stack.append(parentstack)
        return self.code.eval(stack)
    def __call__(self, *args):
        return Call(self, *(Const(a) for a in args)).eval([])

class Closure(object):
    def __init__(self, code):
        self.code = code
    def eval(self, stack):
        return BoundClosure(self.code, stack)

class BoundClosure(Func):
    def __init__(self, code, stack):
        self.code = code
        self.stack = stack
    def eval(self, stack):
        return self
    def invoke(self, stack, parentstack):
        stack.append(self.stack)
        return self.code.eval(stack)

class Call(object):
    def __init__(self, func, *args):
        self.func = func
        self.args = args
    def eval(self, stack):
        if type(self.func) is str:
            func = Func.registry[self.func]
        else:
            func = self.func
        self.func = func
        func = func.eval(stack)
        stack2 = [a.eval(stack) for a in self.args]
        return func.invoke(stack2, stack)

class Const(object):
    def __init__(self, value):
        self.value = value
    def eval(self, stack):
        return self.value

class Arg(object):
    def __init__(self, index):
        self.index = index
    def eval(self, stack):
        return stack[self.index]

class BoundArg(object):
    def __init__(self, frame, index):
        self.frame = frame
        self.index = index
    def eval(self, stack):
        for i in range(self.frame):
            stack = stack[-1]
        return stack[self.index]

class Add(object):
    def __init__(self, first, *args):
        self.first = first
        self.args = args
    def eval(self, stack):
        v = self.first.eval(stack)
        for a in self.args:
            v += a.eval(stack)
        return v
class Sub(object):
    def __init__(self, first, *args):
        self.first = first
        self.args = args
    def eval(self, stack):
        v = self.first.eval(stack)
        for a in self.args:
            v -= a.eval(stack)
        return v

class Mul(object):
    def __init__(self, first, *args):
        self.first = first
        self.args = args
    def eval(self, stack):
        v = self.first.eval(stack)
        for a in self.args:
            v *= a.eval(stack)
        return v

class Div(object):
    def __init__(self, first, *args):
        self.first = first
        self.args = args
    def eval(self, stack):
        v = self.first.eval(stack)
        for a in self.args:
            v /= a.eval(stack)
        return v

class If(object):
    def __init__(self, cond, thencode, elsecode):
        self.cond = cond
        self.thencode = thencode
        self.elsecode = elsecode
    def eval(self, stack):
        if self.cond.eval(stack):
            return self.thencode.eval(stack)
        else:
            return self.elsecode.eval(stack)

class Comparator(object):
    def __init__(self, lhs, rhs):
        self.lhs = lhs
        self.rhs = rhs
    def eval(self, stack):
        return self.cmp(self.lhs.eval(stack), self.rhs.eval(stack))
    def cmp(self, lhs, rhs):
        return False

class Eq(Comparator):
    def cmp(self, lhs, rhs):
        return lhs == rhs

class Ne(Comparator):
    def cmp(self, lhs, rhs):
        return lhs != rhs

class Lt(Comparator):
    def cmp(self, lhs, rhs):
        return lhs < rhs

class Gt(Comparator):
    def cmp(self, lhs, rhs):
        return lhs > rhs

class Le(Comparator):
    def cmp(self, lhs, rhs):
        return lhs <= rhs

class Ge(Comparator):
    def cmp(self, lhs, rhs):
        return lhs >= rhs

class Print(object):
    def __init__(self, expr):
        self.expr = expr
    def eval(self, stack):
        res = self.expr.eval(stack)
        print res
        return res

class Bot(object):
    def __init__(self, energy, code):
        self.energy = energy
        self.code = code
    @classmethod
    def create_random(cls):
        return cls(code)
    def duplicate(self):
        pass
    def mutate(self):
        pass
    def play(self):
        pass

class World(object):
    def __init__(self, width, height, foodcount, bots):
        self.width = width
        self.height = height
        self.foodcount = foodcount
        self.bots = bots
    def cycle(self):
        for bot in self.bots:
            bot.play()
            if bot.energy == 0:
                bot = 0
            newbots.append(bot)
        self.bots = newbots


f = Func("fact",
    If(Le(Arg(0), Const(1)),
        Const(1),
        Mul(Arg(0), Call("fact", Sub(Arg(0), Const(1)))),
    )
)

g = Func("single_adder",
    Closure(
        Add(Arg(0), BoundArg(1, 0))
    )
)

h = Func("bi_adder", 
    Call(Call(g, Arg(0)), Arg(1))
)


print f(5)
print g(3)(4)
print h(4, 9)

















